home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / scvs / scvs.mary < prev    next >
Text File  |  1991-10-31  |  51KB  |  2,200 lines

  1. #! /sprite/cmds/perl 
  2. #
  3. #   Scvs is the "Sprite Concurrent Version System", pronounced "skivies".
  4. #   It is a Perl script wrapper for cvs.  See the cvs man page for more
  5. #   details.
  6. #
  7. # $Header: /local/src/cmds/scvs/RCS/scvs,v 1.10 91/10/08 17:21:06 jhh Exp $ SPRITE (Berkeley)
  8. #
  9. # Copyright 1991 Regents of the University of California
  10. # Permission to use, copy, modify, and distribute this
  11. # software and its documentation for any purpose and without
  12. # fee is hereby granted, provided that this copyright
  13. # notice appears in all copies.  The University of California
  14. # makes no representations about the suitability of this
  15. # software for any purpose.  It is provided "as is" without
  16. # express or implied warranty.
  17. #
  18.  
  19. require "option.pl";
  20. require "pwd.pl";
  21. require "ctime.pl";
  22. require "stat.pl";
  23.  
  24. $recurse = 1;                # A John Hartman special word.
  25. $verbose = 0;
  26. $linkFile = "links";
  27. $debug = 0;
  28. $configFile = "SCVS.config";
  29. $argFile = "args";
  30. $modNameFile = "moduleName";
  31. $userFile = "SCVS/users";
  32. $installOp = 0;
  33.  
  34. @options = (
  35.     $OPT_NIL, $OPT_DOC, $OPT_NIL, 
  36.     "Usage: scvs [scvs options] command [command options]",
  37.     "V", $OPT_TRUE, *verbose, "Verbose",
  38.     "D", $OPT_TRUE, *debug, "Debug",
  39.     "r", $OPT_FUNC, "CvsOpt1", "Check out files read-only",
  40.     "w", $OPT_FUNC, "CvsOpt1", "Check out files read-write (default)",
  41.     "v", $OPT_FUNC, "CvsOpt1", "Print cvs version info",
  42.     "d", $OPT_STRING, *cvsroot, "Specify cvs root directory",
  43.     "e", $OPT_FUNC, "CvsOpt1", "Specify editor to use",
  44.     "H", $OPT_FUNC, "CvsOpt1", "Print help information",
  45. );
  46. undef($cvsargs);
  47. &Opt_Parse(*ARGV, @options, $OPT_OPTIONS_FIRST);
  48. if ($debug) {
  49.     $verbose = 1;
  50. }
  51. $cvsCmdArgs = $cvsargs;
  52.  
  53. @cvsCmds = ("join", "patch", "tag");
  54.  
  55.  
  56. #
  57. # Config
  58. #
  59. # Find the configuration file and set up various configuration variables.
  60. #
  61. # Results: 0 if successful, 1 otherwise
  62. # Side effects: Some variables are set.
  63. #
  64.  
  65. sub Config {
  66.     local($pwd) = $ENV{'PWD'};
  67.     local($stat, $lastStat) = (0, 0);
  68.     local($tmp);
  69.     local(@attempts);
  70.  
  71.     #
  72.     # Work our way up the directory tree looking for the config file.
  73.     #
  74.     while(! -e $configFile) {
  75.     push(@attempts, $ENV{'PWD'});
  76.     &Chdir("..") == 0 || return 1;
  77.     &Stat(".");
  78.     $stat = $st_dev . $st_ino . $st_serverID;
  79.     last if ($stat eq $lastStat);
  80.     $lastStat = $stat;
  81.     }
  82.     if (! -e $configFile) {
  83.     printf("Couldn't find configuration file\n");
  84.     foreach $tmp (@attempts) {
  85.         printf("Not in $tmp\n");
  86.     }
  87.     return 1;
  88.     }
  89.     open(CONFIG, "$configFile") || die("Can't open $configFile: $!\n");
  90.     while(<CONFIG>) {
  91.     next if (/^\s*#/);
  92.     if (/^cvsroot:\s+(\S+)\s*$/) {
  93.         if (!defined($cvsroot)) {
  94.         $cvsroot = $1;
  95.         }
  96.     } elsif(/^installdir:\s+(\S+)\s*$/) {
  97.         $installdir = $1;
  98.     }
  99.     }
  100.     close(CONFIG);
  101.     if (!defined($cvsroot)) {
  102.     printf("cvsroot not set in config file\n");
  103.     return 1;
  104.     }
  105.     &Chdir("$pwd") == 0 || return 1;
  106.     return 0;
  107. }
  108.  
  109. #
  110. # PackCmd($command, @dirs)
  111. #
  112. # Runs a Pack or Unpack command on each of the directories in the list.
  113. #
  114. # Results: 0 if successful, 1 otherwise
  115. #
  116. # Side effects:  The link file is modified.
  117. #
  118.  
  119. sub PackCmd {
  120.     local($command) = shift;
  121.     local(@dirs) = @_;
  122.     local($status) = 0;
  123.     local($pwd) = $ENV{'PWD'};
  124.  
  125.     if ($#dirs < $[) {
  126.     push(@dirs, '.');
  127.     }
  128.     foreach $dir (@dirs) {
  129.     &Chdir($dir) == 0 || return 1; 
  130.     if ($command eq "pack") {
  131.         $status = &Pack($dir);
  132.     } else {
  133.         $status = &Unpack($dir);
  134.     }
  135.     if ($status) {
  136.         return $status;
  137.     }
  138.     &Chdir($pwd) == 0 || return 1; 
  139.     }
  140. }
  141. #
  142. # Pack($path)
  143. #
  144. # Finds all symbolic links in the current directory and puts them in the
  145. # link file.  The links are stored in alphabetical
  146. # order.  If $recurse is non-zero, Pack will call itself to recurse on
  147. # subdirectories.
  148. #
  149. # Results: 0 if successful, 1 otherwise
  150. #
  151. # Side effects: The link file is modified.
  152. #
  153.  
  154. sub Pack {
  155.     local($path) = shift;
  156.     local($addDir) = 0;
  157.     local($addFile) = 0;
  158.     local(%links);
  159.     local($link);
  160.  
  161.     #
  162.     # Don't pack SCVS subdirectories.
  163.     #
  164.     if ($path =~ m|.*/SCVS|) {
  165.     return 0;
  166.     }
  167.     printf(STDERR "Packing $path\n") if ($debug);
  168.     $addDir = (-d "SCVS") ? 0 : 1;
  169.     $addFile = (-f "SCVS/$linkFile") ? 0 : 1;
  170.     opendir(THISDIR, ".") || return &Error(1, "Opendir of $path failed: $!\n");
  171.     foreach $link (grep(-l, readdir(THISDIR))) {
  172.     printf(STDERR "$link\n") if ($debug);
  173.     $links{$link} = readlink($link);
  174.     }
  175.     close(THISDIR);
  176.     if (defined(%links) || (!$addFile)) {
  177.     if ($addDir) {
  178.         mkdir("SCVS", 0770) ||
  179.         return &Error(1, "Mkdir of SCVS failed: $!\n");
  180.     }
  181.     if (open(PACK, ">SCVS/$linkFile") == 0) {
  182.         printf("Can't open $linkFile: $!\n");
  183.         $status = 1;
  184.         last;
  185.     }
  186.     printf(PACK 
  187.         "# This file is used by scvs and contains symbolic link\n");
  188.     printf(PACK 
  189.         "# information.  Each line is of the form \"link target\"\n");
  190.     printf(PACK "# \$Header\n");
  191.     foreach $link (sort keys %links) {
  192.         printf(PACK "%-24s %s\n", $link, $links{$link});
  193.     }
  194.     close(PACK);
  195.     if ($addFile && (-e "CVS.adm")) {
  196.         if ($addDir) {
  197.         system("cvs -d $cvsroot add SCVS");
  198.         }
  199.         system("cvs -d $cvsroot add -m\"scvs links\" SCVS/$linkFile");
  200.     }
  201.     } 
  202.     if ($recurse) {
  203.     $status = &AllSubdirs($path, "Pack");
  204.     }
  205.     return $status;
  206. }
  207.  
  208. #
  209. # Unpack($path)
  210. #
  211. # Reads the link file in the current directory and creates symbolic links
  212. # from its contents. If recurse is non-zero, Unpack will call itself to 
  213. # recurse on subdirectories.
  214. #
  215. # Results: 0 if successful, 1 otherwise
  216. #
  217. # Side effects: Symbolic links may be created in the current directory
  218. #
  219. sub Unpack {
  220.     local($path) = shift;
  221.     local($status) = 0;
  222.  
  223.     printf(STDERR "Unpacking $path\n") if ($debug);
  224.     if (open(UNPACK, "SCVS/$linkFile")) {
  225.     while(<UNPACK>) {
  226.         next if (/^#/);
  227.         if (/(\S+)\s+(\S+)/) {
  228.         ($link, $value) = ($1, $2);
  229.         if (-l $link) {
  230.             $old = readlink($link);
  231.             if ($old ne $value) {
  232.             printf(
  233.             "Changing $link -> $value, instead of -> $old\n");
  234.             unlink($link);
  235.             } else {
  236.             next;
  237.             }
  238.         } elsif (-e $link) {
  239.             printf("File $link already exists.\n");
  240.             $status = 1;
  241.             next;
  242.         } elsif ($verbose) {
  243.             printf("Creating: $link -> $value\n");
  244.         }
  245.         if (symlink($value, $link) == 0) { 
  246.             printf("Can't create link from $link to $value: $!");
  247.             $status = 1;
  248.         }
  249.         }
  250.     }
  251.     close(UNPACK);
  252.     }
  253.     if ($recurse) {
  254.     $status = &AllSubdirs($path, "Unpack");
  255.     }
  256.     return $status;
  257. }
  258.  
  259. #
  260. # Repository(module)
  261. #
  262. # Finds the pathname of the repository directory for the given module.
  263. #
  264. # Results: The pathname
  265. #
  266. # Side effects: 
  267. #
  268.  
  269. sub Repository {
  270.     local($tmp);
  271.     $tmp = &ReadFile("$_[0]/CVS.adm/Repository", 1);
  272.     if (defined($tmp)) {
  273.     chop($tmp);
  274.     return "$cvsroot/$tmp"; 
  275.     }
  276.     return undef;
  277. }
  278.  
  279. #
  280. # Prune($path)
  281. #
  282. # Removes the given directory if it is empty (no user files or subdirectories).
  283. # Recurses on subdirectories.
  284. #
  285. # Results: 0 if successful, 1 otherwise
  286. #
  287. # Side effects: The directory or its subdirectories may be removed.
  288. #
  289.  
  290. sub Prune {
  291.     local($path) = shift;
  292.     local($i);
  293.     local($status) = 0;
  294.     local($empty) = 1;
  295.     local($tail) = substr($path, rindex($path, '/') + 1);
  296.  
  297.     if ($tail eq "SCVS") {
  298.     return 0;
  299.     }
  300.     print "Pruning $path\n" if ($debug);
  301.     $status = &AllSubdirs($path, "Prune");
  302.     if ($status) {
  303.     return $status;
  304.     }
  305.     opendir(THISDIR, ".") || 
  306.     return &Error(1, "Opendir of $path failed: $!\n"); 
  307.     foreach $i (readdir(THISDIR)) {
  308.     next if ($i eq ".");
  309.     next if ($i eq "..");
  310.     next if ($i eq "CVS.adm");
  311.     next if ($i eq "SCVS");
  312.     print "Found $i in $path\n" if ($debug);
  313.     $empty = 0;
  314.     last;
  315.     }
  316.     close(THISDIR);
  317.     if ($empty) {
  318.     print "Prune: chdir to ..\n" if ($debug);
  319.     &Chdir("..") == 0 || return 1;
  320.     print "Prune: deleting $tail\n" if ($debug);
  321.     system("rm -rf $tail");
  322.     }
  323.     return 0;
  324. }
  325.  
  326. #
  327. # CvsOpt1($optString, $nextArg)
  328. #
  329. # Appends $optString to $cvsargs.
  330. #
  331. # Results: 0 
  332. #
  333. # Side effects: None
  334. #
  335. sub CvsOpt1 {
  336.     printf("CvsOpt1 @_\n") if ($debug);
  337.     $cvsargs .= "$_[0] ";
  338.     return 0;
  339. }
  340.  
  341. #
  342. # CvsOpt2($optString, $nextArg)
  343. #
  344. # Appends $optString and $nextArg to $cvsargs.
  345. #
  346. # Results: 1
  347. #
  348. # Side effects: None
  349. #
  350. sub CvsOpt2 {
  351.     printf("CvsOpt2 @_\n") if ($debug);
  352.     $cvsargs .= "$_[0] \"$_[1]\" ";
  353.     return 1;
  354. }
  355.  
  356.  
  357. #
  358. # Checkout(@modules)
  359. #
  360. # Checks out modules.  "cvs co" is used to make a copy of the module. 
  361. # Unpack is used to unpack symbolic links.  
  362. # The current user name is added to the SCVS.users
  363. # file and a list of any other users with a copy of the module are 
  364. # printed.  Any options passed to "cvs co" are stored in the SCVS/args
  365. # file to be used on subsequent updates.
  366. #
  367. # Results: 0 if successful, 1 otherwise
  368. #
  369. # Side effects: A subdirectory is created for each module.
  370. #
  371.  
  372. sub Checkout {
  373.     local(@modules) = @_;
  374.     local($buffer, $i,$repos, $user, $date, %count, %dates);
  375.     local($found, $name);
  376.     local($prune) = 1;
  377.     local($personal) = 0;
  378.     local($args);
  379.     local(@options) = ( 
  380.     "l", $OPT_FALSE, *recurse, "Don't recurse.",
  381.     "P", $OPT_FALSE, *prune, "Don't prune empty directories.",
  382.     "i", $OPT_TRUE, *personal, "Deviation from standard source tree",
  383.     "f", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  384.     "c", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  385.     "Q", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  386.     "q", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  387.     "f", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  388.     "n", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  389.     "p", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  390.     "r", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  391.     "D", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  392.      );
  393.  
  394.     undef($cvsargs);
  395.     &Opt_Parse(*modules, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  396.     $args = $cvsargs;
  397.  
  398.     # Put together the "cvs co" command.
  399.  
  400.     $buffer = "cvs -d $cvsroot $cvsCmdArgs co $args";
  401.  
  402.     if ($args =~ /-c/) {
  403.     system("$buffer");
  404.     return 0;
  405.     }
  406.    if (($args =~ /-r/) || ($args =~ /-D/)) {
  407.     $buffer .= "-f ";
  408.     }
  409.     $status = &Lock("r", @modules);
  410.     if ($status) {
  411.     return $status;
  412.     }
  413.     $user = getlogin;
  414.     print "@modules\n" if ($debug);
  415.  
  416. module:
  417.     foreach $i (@modules) {
  418.     local($pwd) = $ENV{'PWD'};
  419.  
  420.     printf("Checking out $i\n") if ($debug);
  421.     # Perform the "cvs co".
  422.  
  423.     printf("$buffer $i \n") if ($debug);
  424.     system("$buffer $i");
  425.  
  426.     # Store the "cvs co" arguments in the info file.
  427.  
  428.     if (! -d "$i/SCVS") {
  429.         if (!mkdir("$i/SCVS", 0770)) {
  430.         $status = &Error(1, "Mkdir of $i/SCVS failed: $!\n");
  431.         next module;
  432.         }
  433.     }
  434.     if (!open(CO, ">$i/SCVS/$argFile")) {
  435.         $status = &Error(1, "Open of $i/SCVS/$argFile failed: $!\n");
  436.         next module;
  437.     }
  438.     print(CO "# This file contains the arguments given when this\n");
  439.     print(CO "# module was checked out.\n");
  440.     print(CO "$cvsCmdArgs\n");
  441.     print(CO "$args\n");
  442.     close(CO);
  443.  
  444.     &Chdir($i) == 0 || return 1; 
  445.  
  446.     # Unpack the module.
  447.     &Unpack($i) == 0 || return &Error("Unpack of $i failed\n");
  448.  
  449.     # Prune any empty directories in the module.
  450.     if ($prune) {
  451.         &Prune($i) == 0 || return &Error(1, "Prune of $i failed\n");
  452.     }
  453.  
  454.     &Chdir($pwd) == 0 || return 1; 
  455.  
  456.     # See if any other users have a copy of the module, and add our
  457.     # own entry.
  458.  
  459.     $repos = &Repository($i);
  460.     next module if (!defined($repos));
  461.     $date = &ctime(time);
  462.     open(CO2, ">$repos/$tmpfile") ||
  463.         return &Error(1, "Open of $repos/$tmpfile failed: $!\n");
  464.     if (-e "$repos/$userFile") {
  465.         local($copy) = 0;
  466.         open(CO1, "$repos/$userFile") ||
  467.         return &Error(1, "Open of $repos/$userFile failed: $!\n");
  468.         while(<CO1>) {
  469.         $copy = 0;
  470.         next if (/^#/);
  471.         if (/^$user\s+([\w\/\.]+)\s+(.*)/) {
  472.             if ($1 eq "$pwd/$i") {
  473.             $copy = 1;
  474.             } else {
  475.             $found = 1;
  476.             push(@mine, $_);
  477.             }
  478.         } elsif (/^(\S+)\s+([\w\/\.]+)\s+(.*)/) {
  479.             $others{$1} = $3;
  480.         }
  481.         }
  482.         continue {
  483.         if (!$copy) {
  484.             print CO2 $_;
  485.         }
  486.         }
  487.         close(CO1);
  488.     } else {
  489.         printf(CO2 "# List of users with copies of this module.\n");
  490.     }
  491.     if ($#mine >= $[) {
  492.         printf("\nYou also have these copies of the $i module:\n");
  493.         print join("\n", @mine);
  494.     }
  495.     printf(CO2 "$user $pwd/$i %s", &ctime(time));
  496.     close(CO2);
  497.     if (!$personal) {
  498.         if (!rename("$repos/$tmpfile", "$repos/$userFile")) {
  499.         printf(
  500.           "Rename of $repos/$tmpfile to $repos/$userFile failed:$!\n");
  501.         unlink("$repos/$tmpfile");
  502.         next module;
  503.         }
  504.     } else {
  505.         unlink("$repos/$tmpfile");
  506.     }
  507.     if (defined(%others)) {
  508.         printf("\nThe following users have copies of the $i module:\n"); 
  509.         while(($name, $date) = each(%others)) {
  510.         printf("$name $date\n");
  511.         }
  512.     }
  513.     }
  514.     return 0;
  515. }
  516.  
  517. #
  518. # UnlockCmd(@ARGV)
  519. #
  520. # Parse arguements, then call Unlock to do the dirty work. 
  521. #
  522. # Results: 0 if successful, 1 otherwise
  523. #
  524. # Side effects: 
  525. #
  526. sub UnlockCmd {
  527.     local(@args) = @_;
  528.     local($all) = 0;
  529.     local($status) = 0;
  530.     local(@options) = (
  531.     "a", $OPT_TRUE, *all, "Remove everybody's locks",
  532.     );
  533.     &Opt_Parse(*args, @options, $OPT_OPTIONS_FIRST);
  534.     $status = &Unlock($all,@args);
  535.     return $status;
  536. }
  537.  
  538.  
  539. #
  540. # Unlock($allusers, @modules)
  541. #
  542. # Remove the locks for a list of modules.  
  543. #
  544. # Results: 0 if successful, 1 otherwise
  545. #
  546. # Side effects: 
  547. #
  548.  
  549. sub Unlock {
  550.     local($allusers) = shift;
  551.     local(@modules) = @_;
  552.     local($cvsdir, $i, $lock);
  553.     local($status) = 0;
  554.     local($user) = getlogin;
  555.  
  556.     print("Unlock $allusers @modules\n") if ($debug);
  557.     if (!defined(%modMap)) {
  558.     &ModMap;
  559.     }
  560.     if ($#modules < $[) {
  561.     push(@modules, ".");
  562.     }
  563. module:
  564.     foreach $i (@modules) {
  565.     if ($i eq ".") {
  566.         $i = &GetModuleName;
  567.         if (!defined($i)) {
  568.         $status = 1;
  569.         next module;
  570.         }
  571.     }
  572.     if (!defined($modMap{$i})) {
  573.         printf(STDERR "Module $i does not exist.\n");
  574.         $status = 1;
  575.         next module;
  576.     }
  577.     $cvsdir = "$cvsroot/$modMap{$i}/SCVS";
  578.     $lock = "$cvsdir/locks";
  579.     if (!-e $lock) {
  580.         next module;
  581.     }
  582.     if ($allusers) {
  583.         if (!unlink($lock)) {
  584.         printf("Can't remove lock file $lock: $!\n");
  585.         }
  586.         next module;
  587.     }
  588.     if (!open(UNLOCK1, "$lock")) {
  589.         print("Open of $lock failed: $!\n");
  590.         next module;
  591.     }
  592.     if (!open(UNLOCK2, ">$cvsdir/$tmpfile")) {
  593.         print("Open of $cvsdir/$tmpfile failed: $!\n");
  594.         next module;
  595.     }
  596.     flock(UNLOCK1, 2) || 
  597.         return &Error(1, "Flock(2) of $lock failed: $!\n");
  598.  
  599.     while(<UNLOCK1>) {
  600.         ($type, $name) = split(' ');
  601.         if ($name ne $user) {
  602.         print(UNLOCK2 $_);
  603.         }
  604.     }
  605.     close(UNLOCK2);
  606.     if (!rename("$cvsdir/$tmpfile", "$lock")) {
  607.         printf(
  608.           "Rename of $cvsdir/$tmpfile to $lock failed:$!\n");
  609.         unlink("$cvsdir/$tmpfile");
  610.         next module;
  611.     }
  612.     }
  613.     return $status;
  614. }
  615.  
  616. #
  617. # LockCmd(@ARGV)
  618. #
  619. # Parse any options then call Lock to do all the work.
  620. #
  621. # Results: 0 if successful, 1 otherwise
  622. #
  623. # Side effects: The lock files in the modules are updated.
  624. #
  625.  
  626. sub LockCmd {
  627.     local(@args) = @_;
  628.     local($write) = 1;
  629.     local($status) = 0;
  630.     local(@options) = (
  631.     "w", $OPT_TRUE, *write, "Write (exclusive) lock",
  632.     "r", $OPT_FALSE, *write, "Read (shared) lock",
  633.     );
  634.     print("LockCmd @args\n") if ($debug);
  635.     &Opt_Parse(*args, @options, $OPT_OPTIONS_FIRST);
  636.     $status = &Lock($write ? "w" : "r", @args);
  637.     undef(@locks);
  638.     return $status;
  639. }
  640.  
  641.  
  642. #
  643. # Lock($type, @modules)
  644. #
  645. # Make sure the modules are unlocked, and lock them.  Any modules that
  646. # we lock are put in the @lock array.  
  647. #
  648. # Results: 0 if successful, 1 otherwise
  649. #
  650. # Side effects: Lock files are created in the modules.
  651. #
  652.  
  653. sub Lock {
  654.     local($type) = shift;
  655.     local(@modules) = @_;
  656.     local($cvsdir);
  657.     local($status) = 0;
  658.     local($i, $name);
  659.     local(@mylocks);
  660.     local($user) = getlogin;
  661.     local(@lockFiles);
  662.     local($prevType);
  663.     local($prevName);
  664.     local($prevDate);
  665.     local(@prevLocks);
  666.     local($lock);
  667.  
  668.     print("Lock $type @modules\n") if ($debug);
  669.     if (!defined(%modMap)) {
  670.     &ModMap;
  671.     }
  672.     if ($#modules < $[) {
  673.     push(@modules, ".");
  674.     }
  675. module:
  676.     foreach $i (@modules) {
  677.     if ($i eq ".") {
  678.         $i = &GetModuleName;
  679.         if (!defined($i)) {
  680.         $status = 1;
  681.         next module;
  682.         }
  683.     }
  684.     if (!defined($modMap{$i})) {
  685.         printf(STDERR "$i module does not exist.\n");
  686.         $status = 1;
  687.         next module;
  688.     }
  689.     $cvsdir = "$cvsroot/$modMap{$i}/SCVS";
  690.     $lock = "$cvsdir/locks";
  691.     print("Cvsdir = $cvsdir\n") if ($debug);
  692.     if (-f "$lock") {
  693.         print("Opening $lock\n") if ($debug);
  694.         open(LOCK1, "$lock") || 
  695.         return &Error(1, "Open of $lock failed: $!\n");
  696.         flock(LOCK1, 2) || 
  697.         return &Error(1, "Flock(2) of $lock failed: $!\n");
  698.         while(<LOCK1>) {
  699.         ($prevType, $prevName) = split(' ');
  700.         if ($prevName eq $user) {
  701.             if ($prevType ne $type) {
  702.             return &Error(1, "$i already locked:\n$_");
  703.             } else {
  704.             close(LOCK1);
  705.             next module;
  706.             }
  707.         } else {
  708.             if (($prevType eq "r") && ($type eq "w")) {
  709.             return &Error(1, "$i already locked:\n$_");
  710.             } elsif ($prevType eq "w") {
  711.             return &Error(1, "$i already locked:\n$_");
  712.             }
  713.         }
  714.         push(@prevLocks, $_);
  715.         }
  716.     }
  717.     open(LOCK2, ">$cvsdir/$tmpfile") ||
  718.         return &Error(1, "Open of $cvsdir/$tmpfile failed: $!\n");
  719.     foreach $i (@prevLocks) {
  720.         print(LOCK2 "$i");
  721.     }
  722.     printf(LOCK2 "$type $user %s", &ctime(time));
  723.     close(LOCK2);
  724.     if (!rename("$cvsdir/$tmpfile", "$lock")) {
  725.         printf(
  726.           "Rename of $cvsdir/$tmpfile to $lock failed:$!\n");
  727.         unlink("$cvsdir/$tmpfile");
  728.         return 1;
  729.     }
  730.     push(@mylocks, $i);
  731.     close(LOCK1);
  732.     }
  733.     if ($status) {
  734.     if (&Unlock(0, @mylocks)) {
  735.         return &Error(1, "Can't clean up in LockCmd\n");
  736.     }
  737.     }
  738.     push(@locks, @mylocks);
  739.     return $status;
  740. }
  741.  
  742. #
  743. # UpdateCmd($lock, @names)
  744. #
  745. # Update modules.  If the arguments are a list of subdirectories then
  746. # we chdir to each of them and run "cvs update".  If the arguments are
  747. # a list of files then we pass them to cvs.  If no files or directories
  748. # are specified then we update the current directory.  The arguments
  749. # for update are retrieved from the SCVS/args file.
  750. #
  751. # Results: 0 if successful, 1 otherwise
  752. #
  753. # Side effects: 
  754. #
  755.  
  756. sub UpdateCmd {
  757.     local($lock) = shift;
  758.     local(@names) = @_;
  759.     local($buffer, $i, $cvsdir, $date, %count, %dates);
  760.     local($found, $name);
  761.     local($module);
  762.     local($pwd);
  763.     local($tmp);
  764.     local($prune);
  765.     local($buildDirs) = 1;
  766.     local($args);
  767.     local(@options) = ( 
  768.     "B", $OPT_FALSE, *buildDirs, "Don't create new directories.",
  769.     "l", $OPT_FALSE, *recurse, "Don't recurse on subdirs",
  770.     "Q", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  771.     "q", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  772.     "f", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  773.     "n", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  774.     "p", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  775.     "d", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  776.     "r", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  777.     "D", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  778.     );
  779.  
  780.     undef($cvsargs);
  781.     &Opt_Parse(*names, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  782.     $args = $cvsargs;
  783.  
  784.     # Put together the "cvs update" command.
  785.  
  786.     if ($buildDirs) {
  787.     $args .= "-d ";
  788.     }
  789.     if (! $recurse) {
  790.     $args .= "-l ";
  791.     }
  792.     if ($installOp) {
  793.     $buffer = "cvsexec -d $cvsroot $cvsCmdArgs ";
  794.     $installOp = 0;
  795.     } else {
  796.     $buffer = "cvs -d $cvsroot $cvsCmdArgs ";
  797.     }
  798.  
  799.     if ($#names < $[) {
  800.     push(@names, ".");
  801.     }
  802.     if (-f $names[0]) {
  803.     if ($lock) {
  804.         $status = &Lock("r","."); 
  805.         if ($status) {
  806.         return $status;
  807.         }
  808.     }
  809.     $tmp = "$buffer update $args @names";
  810.     printf("$tmp\n") if ($debug);
  811.     system($tmp);
  812.     $recurse = 0;
  813.     &Unpack(".") == 0 ||
  814.         return &Error(1, "Unpack of current directory failed.\n");
  815.     } else {
  816.     #
  817.     # Lock the modules.
  818.     #
  819.     if ($lock) {
  820.         $status = &Lock("r", @names); 
  821.         if ($status) {
  822.         return $status;
  823.         }
  824.     }
  825.     $pwd = $ENV{'PWD'};
  826. module: 
  827.     foreach $i (@names) {
  828.         $prune = 0;
  829.         &Chdir($i) == 0 || return 1; 
  830.         if (-e "SCVS/$argFile") {
  831.         local(@targs);
  832.         @targs = &ReadFile("SCVS/$argFile", 1);
  833.         if ($targs[1] =~ /(.*)-p(.*)/) {
  834.             $targs[1] = "$1 $2";
  835.             $prune = 1;
  836.         }
  837.         chop($targs[0]);
  838.         chop($targs[1]);
  839.         }
  840.         $tmp = "$buffer $targs[0] update $args $targs[1]";
  841.         printf("$tmp\n") if ($debug);
  842.         system($tmp);
  843.         if (&Unpack($i)) {
  844.         printf(STDERR "Unpack of $i failed.\n");
  845.         $status = 1;
  846.         }
  847.         if ($prune) {
  848.         if (&Prune($i)) {
  849.             printf(STDERR "Prune of $i failed.\n");
  850.             $status = 1;
  851.         }
  852.         }
  853.  
  854.         &Chdir($pwd) == 0 || return 1; 
  855.     }
  856.     }
  857.     return $status;
  858. }
  859.  
  860. #
  861. # Changed($path)
  862. #
  863. # Use the "cvs info" command to see if the contents of the current directory
  864. # or its subdirectories have been changed by the user.  The modified
  865. # parameter is set to 1 if they have been.
  866. #
  867. # Results: 0 if successful, 1 otherwise; 0 if not modified, 1 otherwise
  868. #
  869. # Side effects: 
  870. #
  871. sub Changed {
  872.     local($path) = shift;
  873.     local($modified) = 0;
  874.     local($status) = 0;
  875.     if (!-d "CVS.adm") {
  876.     return 0;
  877.     }
  878.     open(CHG, "cvs -d $cvsroot info |") ||
  879.     return &Error(1, "Can't do cvs info on $path: $!\n");
  880.     while (<CHG>) {
  881.     if (/^[MC]\s+(\S+)/) {
  882.         printf("$path/$1 has been modified\n");
  883.         $modified = 1;
  884.     } elsif(/^A\s+(\S+)/) {
  885.         printf("$path/$1 has been added\n");
  886.         $modified = 1;
  887.     } elsif(/^R\s+(\S+)/) {
  888.         printf("$path/$1 has been deleted\n");
  889.         $modified = 1;
  890.     }
  891.     }
  892.     close(CHG);
  893.     ($status, @results) = &AllSubdirs($path, "Changed");
  894.     if ($status) {
  895.     return $status;
  896.     }
  897.     while ($#results >= $[) {
  898.     local($substatus) = shift(@results);
  899.     local($submod) = shift(@results);
  900.     if ($substatus) {
  901.         $status = 1;
  902.     }
  903.     if ($submod) {
  904.         $modified = 1;
  905.     }
  906.     }
  907.     return ($status, $modified);
  908. }
  909.  
  910. #
  911. # DoneCmd(@modules)
  912. #
  913. # Process the "done" command.  The user is deleted from the list of users
  914. # for each module.  If the -d flag is specified then the snapshot is
  915. # deleted as well.  If the user has made changes to the snapshot the user
  916. # is warned before the "done" command is completed.
  917. #
  918. # Results: 0 if successful, 1 otherwise
  919. #
  920. # Side effects: 
  921. #
  922. sub DoneCmd {
  923.     local(@modules) = @_;
  924.     local($status) = 0;
  925.     local($i);
  926.     local($me) = getlogin;
  927.     local($pwd) = $ENV{'PWD'};
  928.     local($repos, $found);
  929.     local($delete);
  930.     local($modified);
  931.     local(@options) = (
  932.     "d", $OPT_TRUE, *delete, "Delete module",
  933.     );
  934.  
  935.     $recurse = 1;
  936.     undef($cvsargs);
  937.     &Opt_Parse(*modules, @options, $OPT_OPTIONS_FIRST); 
  938.     if ($#modules < $[) {
  939.     return &Error(1, "Done command requires a list of modules\n");
  940.     }
  941.     # Make sure all the modules are unlocked, then lock them.
  942.     $status = &Lock("r",@modules); 
  943.     if ($status) {
  944.     return $status;
  945.     }
  946. module:
  947.     foreach $i (@modules) {
  948.     $ok = 0;
  949.     if (! -d $i) {
  950.         printf("Directory $i not found.\n");
  951.         next module;
  952.     }
  953.     &Chdir($i) == 0 || return 1; 
  954.     ($status, $modified) = &Changed($i);
  955.     if ($status) {
  956.         printf(STDERR "Unable to determine if $i module has changed.\n");
  957.         $modified = 1;
  958.     }
  959.     if ($modified == 1) {
  960.         printf("Do you wish to continue? [y/n] ");
  961. prompt:
  962.         while(1) {
  963.         $answer = <STDIN>;
  964.         chop($answer);
  965.         last prompt if ($answer eq "y");
  966.         next module if ($answer eq "n");
  967.         printf("Please answer with \"y\" or \"n\": ");
  968.         }
  969.     } elsif ($modified == 1) {
  970.         next module;
  971.     }
  972.  
  973.     # Update the user file.
  974.     $repos = &Repository(".");
  975.     next module if (!defined($repos));
  976.     if (!open(DONE1, "$repos/$userFile")) {
  977.         printf("Module $i is not checked out\n");
  978.         next module;
  979.     }
  980.     if (!open(DONE2, ">$repos/$tmpfile")) {
  981.         printf("Can't open $repos/$tmpfile: $!\n");
  982.         $status = 1;
  983.         next module;
  984.     }
  985.     $me = getlogin;
  986.     $found = 0;
  987.     while (<DONE1>) {
  988.         if (/^$me\s+([\w\/\.]+)\s+(.*)/) {
  989.         if ($1 eq "$pwd/$i") {
  990.             $found = 1;
  991.             next;
  992.         }
  993.         }
  994.         print DONE2 $_;
  995.     }
  996.     close(DONE1);
  997.     close(DONE2);
  998.     if (!$found) {
  999.         printf("Module $i is not checked out\n");
  1000.         next module;
  1001.     }
  1002.     if (!rename("$repos/$tmpfile", "$repos/$userFile")) {
  1003.         printf("Rename of $repos/$tmpfile to $repos/$userFile failed:$!\n");
  1004.         unlink("$repos/$tmpfile");
  1005.         next module;
  1006.     }
  1007.     $ok = 1;
  1008.     }
  1009.     continue {
  1010.     &Chdir($pwd) == 0 || return 1; 
  1011.     if ($ok && $delete) {
  1012.         system("rm -rf $i");
  1013.         if ($?) {
  1014.         printf("Delete of $i failed: $?\n");
  1015.         }
  1016.     }
  1017.     }
  1018.     return $status;
  1019. }
  1020.  
  1021. #
  1022. # AllSubdirs(path, routine, args)
  1023. #
  1024. # Call a routine for each subdirectory of the current directory. The
  1025. # current working directory is changed to the subdirectory before the 
  1026. # routine is called, and the path is modified to reflect this change.
  1027. # The path is passed to the routine when it is called. The routine is
  1028. # called for all subdirectories even if one returns an non-zero status,
  1029. # although this function will then return a non-zero status.
  1030. # Any additional arguments for the routine are passed after the path
  1031. # argument.
  1032. #
  1033. # Results: 0 if successful, 1 if the routine returned non-zero for any
  1034. #         of the subdirectories.
  1035. #
  1036. # Side effects: 
  1037. #
  1038. sub AllSubdirs {
  1039.     local($path) = shift;
  1040.     local($routine) = shift;
  1041.     local($pwd) = $ENV{'PWD'};
  1042.     local($substatus);
  1043.     local($dir);
  1044.     local(@results);
  1045.     local(@status);
  1046.     local(@subdirs);
  1047.  
  1048.     printf(STDERR "AllSubdirs of $routine on $pwd\n") if ($debug);
  1049.     opendir(THISDIR, ".") || 
  1050.     return &Error(1, "Opendir of $path failed: $!\n"); 
  1051.     @subdirs = grep((-d) && (!/^\./) && (! -l) && ($_ ne 'CVS.adm'), 
  1052.             readdir(THISDIR));
  1053.     print("AllSubdirs: @subdirs\n") if ($debug);
  1054.     close(THISDIR);
  1055.     print "@subdirs\n****\n" if ($debug); 
  1056.     foreach $dir (@subdirs) {
  1057.     printf("\t$dir\n") if ($debug);
  1058.     &Chdir($dir) == 0 || return 1; 
  1059.     push(@results, &$routine($path . "/$dir", @_));
  1060.     &Chdir($pwd) == 0 || ($status = 1); 
  1061.     }
  1062.     if (wantarray) {
  1063.     return ($status, @results);
  1064.     }
  1065.     if ($status) {
  1066.     return $status;
  1067.     }
  1068.     @status = grep("$_ != 0", @results);
  1069.     if ($#status >= $[) {
  1070.     return $status[0];
  1071.     }
  1072.     return 0;
  1073. }
  1074.  
  1075.  
  1076. #
  1077. # VerifyCurrent($path, *stale, *modified)
  1078. #
  1079. # Check the status of the files in the current directory and its 
  1080. # subdirectories to see if they are out of date.
  1081. #
  1082. # Results: 0 if successful, 1 otherwise;
  1083. #
  1084. # Side effects: 
  1085. #
  1086. sub VerifyCurrent {
  1087.     local($path) = shift;
  1088.     local(*stale) = shift;
  1089.     local(*modified) = shift;
  1090.     local($pwd) = $ENV{'PWD'};
  1091.     local($status) = 0;
  1092.     local($substatus) = 0;
  1093.     local($current) = 1;
  1094.     local($mod) = 0;
  1095.  
  1096.     printf("Verifying that $path is current\n") if ($debug);
  1097.     if (!-d "CVS.adm") {
  1098.     return 0;
  1099.     }
  1100.     open(CHK, "cvs -d $cvsroot info |") ||
  1101.     return &Error(1, "Can't get info for $path: $!\n");
  1102.     while(<CHK>) {
  1103.     if (/^U\s+(\S+)/) {
  1104.         printf("File $path/$1 is out of date or needs to be added.\n");
  1105.         $current = 0;
  1106.     } elsif (/^D\s+(\S+)/) {
  1107.         printf("File $path/$1 has been removed from the repository.\n");
  1108.         $current = 0;
  1109.     } elsif (/^C\s+(\S+)/) {
  1110.         printf("File $path/$1 is out of date.\n");
  1111.         $current = 0;
  1112.     } elsif (/^[MARC]/) {
  1113.         $mod = 1;
  1114.     } 
  1115.     }
  1116.     close(CHK);
  1117.     if (!$current) {
  1118.     printf("$path is not current\n") if ($debug);
  1119.     push(@stale, $path);
  1120.     }
  1121.     if ($mod) {
  1122.     printf("$path has been modified\n") if ($debug);
  1123.     push(@modified, $path);
  1124.     }
  1125.     if ($recurse) {
  1126.     $status = &AllSubdirs($path, "VerifyCurrent", *stale, *modified);
  1127.     }
  1128.     return $status;
  1129. }
  1130.  
  1131. #
  1132. # UpdateInstalled(@files)
  1133. #
  1134. # Update the installed copy of the sources.  This is done on commit.
  1135. # If @files is not specified then the entire directory and its subdirectories
  1136. # are updated.
  1137. #
  1138. # Results: 0 if successful, 1 otherwise
  1139. #
  1140. # Side effects: The installed sources are updated.
  1141. #
  1142. sub UpdateInstalled {
  1143.     local(@files) = @_;
  1144.     local($dir);
  1145.     local($pwd) = $ENV{'PWD'};
  1146.     local($saveArgs) = $cvsCmdArgs;
  1147.  
  1148.     printf(STDERR "UpdateInstalled\n") if ($debug);
  1149.     $cvsCmdArgs = "-r";
  1150.     $dir = &ReadFile("CVS.adm/Repository", 1);
  1151.     if (!defined($dir)) {
  1152.     return 1;
  1153.     }
  1154.     chop($dir);
  1155.     &Chdir("$installdir/$dir") == 0 || return 1;
  1156.     $installOp = 1;
  1157.     &UpdateCmd(0, "-Q", @files) == 0 || return 1;
  1158.     &Chdir("$pwd") == 0 || return 1;
  1159.     $cvsCmdArgs = $saveArgs;
  1160.     return 0;
  1161. }
  1162.  
  1163.  
  1164.  
  1165. #
  1166. # Commit
  1167. #
  1168. # Commit the current directory and its subdirectories.
  1169. #
  1170. # Results: 0 if successful, 1 otherwise
  1171. #
  1172. # Side effects: 
  1173. #
  1174. sub Commit {
  1175.     local($path) = shift;
  1176.     local($args) = shift;
  1177.     local($pwd) = $ENV{'PWD'};
  1178.     local($status) = 0;
  1179.     local($output);
  1180.     local($tail);
  1181.  
  1182.  
  1183.     printf(STDERR "CommitDir $path\n") if ($debug);
  1184.     if (!-d "CVS.adm") {
  1185.     return 0;
  1186.     }
  1187.     printf("$path:\n");
  1188.     $tail = substr($path, rindex($path, '/') + 1);
  1189.     #
  1190.     # Before we commit the SCVS links file we remove all the deleted links
  1191.     # from it.
  1192.     #
  1193.     if ($tail eq "SCVS") {
  1194.     if (open(CMTDIR1, "$linkFile")) {
  1195.         open(CMTDIR2, ">$tmpfile") ||
  1196.         return &Error(1, "Open of $path/$tmpfile failed: $!\n");
  1197.         while(<CMTDIR1>) {
  1198.         next if (/^[*]/);
  1199.         print CMTDIR2 $_;
  1200.         }
  1201.         close(CMTDIR1);
  1202.         close(CMTDIR2);
  1203.         if (!rename("$tmpfile", "$linkFile")) {
  1204.         printf("Rename of $tmpfile to $linkFile failed:$!\n");
  1205.         unlink("$tmpfile");
  1206.         return 1;
  1207.         }
  1208.         system("cvs -d $cvsroot $cvsCmdArgs ci -f -m scvs links");
  1209.     }
  1210.     }
  1211.     system("cvs -d $cvsroot $cvsCmdArgs ci -f -a $args");
  1212.     return $status;
  1213. }
  1214.  
  1215. #
  1216. # CommitCmd(@names)
  1217. #
  1218. # Commit any changes to the modules or files. 
  1219. # Otherwise all changed files in the current directory and any subdirectories
  1220. # are committed.  Before anything is committed it is checked that all
  1221. # files are up-to-date.  If they aren't, a message is printed and the
  1222. # commit is not done.
  1223. #
  1224. # Results: 0 if successful, 1 otherwise
  1225. #
  1226. # Side effects: 
  1227. #
  1228.  
  1229. sub CommitCmd {
  1230.     local(@names) = @_;
  1231.     local($pwd, $i);
  1232.     local($status) = 0;
  1233.     local($path);
  1234.     local(@stale, @modified);
  1235.     local($tmp);
  1236.     local($args);
  1237.     local(@options) = (
  1238.     "l", $OPT_FALSE, *recurse, "Don't recurse on subdirs",
  1239.     "f", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1240.     "n", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1241.     "m", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1242.     "r", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1243.     );
  1244.  
  1245.     $recurse = 1;
  1246.     undef($cvsargs);
  1247.     &Opt_Parse(*names, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  1248.     $args = $cvsargs;
  1249.  
  1250.     if ($#names < $[) {
  1251.     push(@names, ".");
  1252.     }
  1253.     $args .= " -q";
  1254.     if (-f $names[0]) {
  1255.     $status = &Lock("w","."); 
  1256.     if ($status) {
  1257.         return $status;
  1258.     }
  1259.     $status = &VerifyCurrent(".", *stale, *modified);
  1260.     if ($status) {
  1261.         return $status;
  1262.     }
  1263.     if ($#stale >= $[) {
  1264.         printf("Update your sources using \"scvs update\".\n");
  1265.         return $status;
  1266.     }
  1267.     $tmp = "cvs -d $cvsroot $cvsCmdArgs ci -f $args @names";
  1268.     system($tmp);
  1269.     $status = &UpdateInstalled(@names);
  1270.     } else {
  1271.     $status = &Lock("w",@names); 
  1272.     if ($status) {
  1273.         return $status;
  1274.     }
  1275.     $pwd = $ENV{'PWD'};
  1276.  
  1277.     #
  1278.     # All the modules and their subdirectories must be up-to-date.
  1279.     #
  1280. module:
  1281.     foreach $i (@names) {
  1282.         &Chdir($i) == 0 || return 1; 
  1283.         $status = &VerifyCurrent($i, *stale, *modified);
  1284.         if ($status) {
  1285.         return $status;
  1286.         }
  1287.         &Chdir($pwd) == 0 || return 1; 
  1288.     }
  1289.     
  1290.     if ($#stale >= $[) {
  1291.         printf("Update your sources using \"scvs update\".\n");
  1292.         return $status;
  1293.     }
  1294.     
  1295.     #
  1296.     # Commit all directories that were modified.
  1297.     #
  1298.     foreach $i (@modified) {
  1299.         &Chdir($i) == 0 || return 1; 
  1300.         $status = &Commit($i, $args);
  1301.         last if ($status);
  1302.         if (defined($installdir)) {
  1303.         $status = &UpdateInstalled;
  1304.         last if ($status);
  1305.         }
  1306.         &Chdir($pwd) == 0 || return 1; 
  1307.     }
  1308.     }
  1309.     return $status;
  1310. }
  1311.  
  1312.  
  1313. #
  1314. # WhoCmd(@modules)
  1315. #
  1316. # Print the names of users who have the modules checked out.
  1317. #
  1318. # Results: 0 if successful, 1 otherwise
  1319. #
  1320. # Side effects: 
  1321. #
  1322.  
  1323. sub WhoCmd {
  1324.     local(@modules) = @_;
  1325.     local($pwd, $i);
  1326.     local($status) = 0;
  1327.     local($cvsdir, @who, $user, %users, $line);
  1328.  
  1329.     if (!defined(%modMap)) {
  1330.     &ModMap;
  1331.     }
  1332.     if ($#modules < $[) {
  1333.     push(@modules, ".");
  1334.     }
  1335.     $status = &Lock("r",@modules); 
  1336.     if ($status) {
  1337.     return $status;
  1338.     }
  1339.     $pwd = $ENV{'PWD'};
  1340.  
  1341. module:
  1342.     foreach $i (@modules) {
  1343.     if ($i eq ".") {
  1344.         $i = &GetModuleName;
  1345.         if (!defined($i)) {
  1346.         $status = 1;
  1347.         next module;
  1348.         }
  1349.     }
  1350.     if (!defined($modMap{$i})) {
  1351.         printf(STDERR "$i module does not exist.\n");
  1352.         $status = 1;
  1353.         next module;
  1354.     }
  1355.     $cvsdir = $cvsroot . "/" . $modMap{$i};
  1356.     @who = &ReadFile("$cvsdir/$userFile", 1);
  1357.     foreach $line (@who) {
  1358.         ($user) = split(' ', $line);
  1359.         $users{$user} = 1;
  1360.     }
  1361.     foreach $user (keys %users) {
  1362.         printf("$user\n");
  1363.     }
  1364.     }
  1365.     return $status;
  1366. }
  1367.  
  1368. #
  1369. # AddCmd(@names)
  1370. #
  1371. # Add a file, directory, or symbolic link to a directory.
  1372. #
  1373. # Results: 0 if successful, 1 otherwise
  1374. #
  1375. # Side effects: 
  1376. #
  1377.  
  1378. sub AddCmd {
  1379.     local(@names) = @_;
  1380.     local($i);
  1381.     local($status) = 0;
  1382.     local(%links);
  1383.     local($pwd) = $ENV{'PWD'};
  1384.     local($module);
  1385.     local($args);
  1386.     local(@options) = (
  1387.     "m", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1388.     );
  1389.  
  1390.     undef($cvsargs);
  1391.     &Opt_Parse(*names, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  1392.     $args = $cvsargs;
  1393.  
  1394.     if ($#names < $[) {
  1395.     return &Error(1, "Add command requires list of files\n");
  1396.     }
  1397.     $module = &GetModuleName;
  1398.     if (!defined($module)) {
  1399.     return 1;
  1400.     }
  1401. name:
  1402.     foreach $i (@names) {
  1403.     if (-l $i) {
  1404.         local($target) = readlink($i);
  1405.         if (!defined($target)) {
  1406.         printf("$i does not exist\n");
  1407.         $status = 1;
  1408.         next name;
  1409.         }
  1410.         if (open(ADD, "SCVS/$linkFile")) {
  1411.         while(<ADD>) {
  1412.             if (/^$i\s+(\S+)/) {
  1413.             if ($target ne $1) {
  1414.                 printf("Link $i already points to $1.\n");
  1415.             } else {
  1416.                 printf("Link $i already added.\n");
  1417.             }
  1418.             $status = 1;
  1419.             close(ADD);
  1420.             next name;
  1421.             }
  1422.         }
  1423.         close(ADD);
  1424.         } elsif (! -f "SCVS/$linkFile") {
  1425.         open(ADD, ">SCVS/$linkFile") ||
  1426.             return &Error(1, "Can't open SCVS/$linkFile: $!\n");
  1427.         printf(ADD 
  1428.         "# This file is used by scvs and contains symbolic link\n");
  1429.         printf(ADD 
  1430.         "# information.  Each line is of the form \"link target\"\n");
  1431.         printf(ADD "# \$Header\n");
  1432.         close(ADD);
  1433.         &Chdir("SCVS") == 0 || return 1; 
  1434.         printf("Adding $linkFile directory\n") if ($debug);
  1435.         system("cvs -d $cvsroot add -m \"sym links\" $linkFile");
  1436.         &Chdir($pwd) == 0 || return 1; 
  1437.         } else {
  1438.         return &Error(1, "Open of SCVS/$linkFile failed: $!\n");
  1439.         }
  1440.         $links{$i} = $target;
  1441.     } else {
  1442.         system("cvs -d $cvsroot $cvsCmdArgs add $args $i");
  1443.         if (-d $i) {
  1444.         # 
  1445.         # If we are adding a directory then we should create an
  1446.         # SCVS subdirectory in it.
  1447.         #
  1448.         if (! -d "$i/SCVS") {
  1449.             mkdir("$i/SCVS", 0770) ||
  1450.             return &Error(1, "Mkdir of $i/SCVS failed: $!\n");
  1451.             &Chdir("$i/SCVS") == 0 || return 1; 
  1452.             open(ADD, ">module") ||
  1453.             return &Error(1, "Open of $i/SCVS/module failed: $!\n");
  1454.             printf(ADD "$module\n");
  1455.             close(ADD);
  1456.             system("cvs -d $cvsroot add module");
  1457.             &Chdir($pwd) == 0 || return 1; 
  1458.         }
  1459.         }
  1460.     }
  1461.     if (defined(%links)) {
  1462.         open(ADD, ">>SCVS/$linkFile") ||
  1463.         return &Error(1, "Open of SCVS/$linkFile failed: $!\n");
  1464.         while (($i, $target) = each(%links)) {
  1465.         printf("Adding link $i -> $target\n") if ($debug);
  1466.         printf(ADD "%-24s %s\n", $i, $target);
  1467.         }
  1468.         close(ADD);
  1469.     }
  1470.     }
  1471.     return $status;
  1472. }
  1473. #
  1474. # RemoveCmd(@names)
  1475. #
  1476. # Removes a file, directory, or symbolic link from a directory.
  1477. #
  1478. # Results: 0 if successful, 1 otherwise
  1479. #
  1480. # Side effects: 
  1481. #
  1482.  
  1483. sub RemoveCmd {
  1484.     local(@names) = @_;
  1485.     local($i);
  1486.     local($status, %links, @delete) = 0;
  1487.  
  1488.     if ($#names < $[) {
  1489.     return &Error(1, "Remove command requires list of files\n");
  1490.     }
  1491.     if (open(RM, "SCVS/$linkFile")) {
  1492.     while(<RM>) {
  1493.         next if (/^#/);
  1494.         if (/^([^*]\S+)\s+(\S+)/) {
  1495.         printf("Found link $1 -> $2\n") if ($debug);
  1496.         $links{$1} = $2;
  1497.         }
  1498.     }
  1499.     close(RM);
  1500.     }
  1501. name:
  1502.     foreach $i (@names) {
  1503.     if (-e $i) {
  1504.         printf("$i still exists, moving to $i.old\n");
  1505.         if (!rename("$i", "$i.old")) {
  1506.         printf("Rename failed: $!\n");
  1507.         $status = 1;
  1508.         next name;
  1509.         }
  1510.     }
  1511.     if (defined($links{$i})) {
  1512.         printf("Putting $i on delete list\n") if ($debug);
  1513.         push(@delete, $i);
  1514.     } else {
  1515.         system("cvs -d $cvsroot $cvsCmdArgs remove $i");
  1516.     }
  1517.     }
  1518.     if ($#delete >= $[) {
  1519.     if (!open(RM1, "SCVS/$linkFile")) {
  1520.         printf("Can't open SCVS/$linkFile: $!\n");
  1521.         $status = 1;
  1522.         next name;
  1523.     }
  1524.     if (!open(RM2, ">$tmpfile")) {
  1525.         printf("Can't open $tmpfile: $!\n");
  1526.         $status = 1;
  1527.         next name;
  1528.     }
  1529. line:
  1530.     while (<RM1>) {
  1531.         if (/^([^#*]\S+)\s+(\S+)/) {
  1532.         for ($i = 0; $i <= $#delete; $i++) {
  1533.             if ($delete[$i] eq $1) {
  1534.             splice(@delete, $i, 1);
  1535.             print RM2 "*$_";
  1536.             next line;
  1537.             }
  1538.         }
  1539.         }
  1540.         print RM2 $_;
  1541.     }
  1542.     close(RM1);
  1543.     close(RM2);
  1544.     if (!rename("$tmpfile", "SCVS/$linkFile")) {
  1545.         printf("Rename of $tmpfile to SCVS/$linkFile failed:$!\n");
  1546.         unlink("$tmpfile");
  1547.         $status = 1;
  1548.     }
  1549.     }
  1550.     return $status;
  1551. }
  1552. #
  1553. # Info($path)
  1554. #
  1555. # Prints out status information for the current directory and recurses
  1556. # on subdirectories.
  1557. #
  1558. # Results: 0 if successful, 1 otherwise
  1559. #
  1560. # Side effects: 
  1561. #
  1562. sub Info {
  1563.     local($path) = shift;
  1564.     local($tail);
  1565.     local($diff) = 0;
  1566.     local($cat) = 0;
  1567.     local($i);
  1568.     local($pwd) = $ENV{'PWD'};
  1569.  
  1570.     if (!-d "CVS.adm") {
  1571.     return 0;
  1572.     }
  1573.     $tail = substr($path, rindex($path, '/') + 1);
  1574.     if ($tail eq "SCVS") {
  1575.     return 0;
  1576.     }
  1577.     system("cvs -d $cvsroot $cvsCmdArgs info");
  1578.     if (-d "SCVS") {
  1579.     &Chdir("SCVS") == 0 || return 1;
  1580.     open(INFO, "cvs -d $cvsroot info |") ||
  1581.         return &Error(1, "Can't do cvs info on $path: $!\n");
  1582.     while(<INFO>) {
  1583.         if (/^[UMC]\s+$linkFile/) {
  1584.         $diff = 1;
  1585.         last;
  1586.         } elsif (/^[AD]\s+$linkFile/) {
  1587.         $cat = 1;
  1588.         last;
  1589.         }
  1590.     }
  1591.     close(INFO);
  1592.     if ($diff) {
  1593.         local(%updated);
  1594.         open(INFO, "cvs -d $cvsroot diff $linkFile |") ||
  1595.         return &Error(1, "Can't do cvs diff on $path/$linkFile: $!\n");
  1596.         while(<INFO>) {
  1597.         if (/^>\s+([^*]\S+)/) {
  1598.             printf("A %s\@\n", $1);
  1599.         } elsif (/^>\s+[*](\S+)/) {
  1600.             printf("R %s\@\n", $1);
  1601.             delete $updated{$1};
  1602.         } elsif (/^<\s+([^*]\S+)/) {
  1603.             $updated{$1} = 1;
  1604.         } elsif (/^<\s+[*](\S+)/) {
  1605.             printf("D %s\@\n", $1);
  1606.         }
  1607.         }
  1608.         close(INFO);
  1609.         foreach $i (keys %updated) {
  1610.         printf("U %s\@\n", $i);
  1611.         }
  1612.     }
  1613.     if ($cat) {
  1614.         open(INFO, "$linkFile") ||
  1615.         return &Error(1, "Open of $linkFile failed: $!\n");
  1616.         while(<INFO>) {
  1617.         next if (/^#/);
  1618.         if (/^([^*]\S+)/) {
  1619.             printf("A %s\@\n", $1);
  1620.         } elsif (/^([*]\S+)/) {
  1621.             printf("R %s\@\n", $1);
  1622.         }
  1623.         }
  1624.         close(INFO);
  1625.     }
  1626.     &Chdir($pwd) == 0 || return 1;
  1627.     }
  1628.     if (($recurse) && ($#files < $[)) {
  1629.     $status = &AllSubdirs($path, "Info");
  1630.     }
  1631. }
  1632.  
  1633. #
  1634. # InfoCmd(@modules)
  1635. #
  1636. # Prints out status information for the given modules.
  1637. #
  1638. # Results: 0 if successful, 1 otherwise
  1639. #
  1640. # Side effects: 
  1641. #
  1642.  
  1643. sub InfoCmd {
  1644.     local(@modules) = @_;
  1645.     local($pwd, $i);
  1646.     local($status) = 0;
  1647.     local(@options) = ("l", $OPT_FALSE, *recurse, "Don't recurse on subdirs");
  1648.  
  1649.     $recurse = 1;
  1650.     undef($cvsargs);
  1651.     &Opt_Parse(*modules, @options, $OPT_OPTIONS_FIRST);
  1652.     print "@modules\n" if ($debug);
  1653.  
  1654.     if ($#modules < $[) {
  1655.     push(@modules, ".");
  1656.     }
  1657.     if (-f $modules[0]) {
  1658.     $status = &Lock("r","."); 
  1659.     if ($status) {
  1660.         return $status;
  1661.     }
  1662.     system("cvs -d $cvsroot $cvsCmdArgs info @modules");
  1663.     } else {
  1664.     $status = &Lock("r",@modules);
  1665.     if ($status) {
  1666.         return $status;
  1667.     }
  1668.     $pwd = $ENV{'PWD'};
  1669.     foreach $i (@modules) {
  1670.         printf("InfoCmd %i\n") if ($debug);
  1671.         &Chdir($i) == 0 || return 1; 
  1672.         $status = &Info($i);
  1673.         if ($status) {
  1674.         return $status;
  1675.         }
  1676.         &Chdir($pwd) == 0 || return 1; 
  1677.     }
  1678.     }
  1679.     return $status;
  1680. }
  1681.  
  1682. #
  1683. # DiffFile($path, $file, $args, $current)
  1684. #
  1685. # Prints out status information for the current directory and recurses
  1686. # on subdirectories.
  1687. #
  1688. # Results: 0 if successful, 1 otherwise
  1689. #
  1690. # Side effects: 
  1691. #
  1692. sub DiffFile {
  1693.     local($path) = shift;    # Current path.
  1694.     local($file) = shift;    # File to diff.
  1695.     local($args) = shift;    # args to cvs diff.
  1696.     local($current) = shift;    # Should we diff with current version.
  1697.     local($tail);
  1698.     local($pwd) = $ENV{'PWD'};
  1699.     local($status) = 0;
  1700.     local($version) = "";
  1701.     local($repository);
  1702.  
  1703.     if (!-d "CVS.adm") {
  1704.     return 0;
  1705.     }
  1706.     $repository = &Repository(".");
  1707.     if (!defined($repository)) {
  1708.     print("Repository not found\n") if ($debug);
  1709.     return 0;
  1710.     }
  1711.     printf("Repository is $repository\n") if ($debug);
  1712.     if (!-e "$repository/$file,v") {
  1713.     return 0;
  1714.     }
  1715.     if ($current) {
  1716.     open(DIFF, "cvs -d $cvsroot status $file |") ||
  1717.         return &Error(1, "Can't get status for $path/$file: $!\n");
  1718.     while(<DIFF>) {
  1719.         if (/^RCS:\s+(\S+)/) {
  1720.         $version = "-r $1";
  1721.         last;
  1722.         }
  1723.     }
  1724.     close(DIFF);
  1725.     }
  1726.     system("cvs -d $cvsroot $cvsCmdArgs diff $version $args $file");
  1727. }
  1728.  
  1729. #
  1730. # Diff($path, $args, $current)
  1731. #
  1732. # Prints out status information for the current directory and recurses
  1733. # on subdirectories.
  1734. #
  1735. # Results: 0 if successful, 1 otherwise
  1736. #
  1737. # Side effects: 
  1738. #
  1739. sub Diff {
  1740.     local($path) = shift;    # Current path.
  1741.     local($args) = shift;    # args to cvs diff.
  1742.     local($current) = shift;    # Should we diff with current version.
  1743.     local($tail);
  1744.     local($pwd) = $ENV{'PWD'};
  1745.     local($file);
  1746.     local($status) = 0;
  1747.  
  1748.     if (!-d "CVS.adm") {
  1749.     return 0;
  1750.     }
  1751.     $tail = substr($path, rindex($path, '/') + 1);
  1752.     if ($tail eq "SCVS") {
  1753.     return 0;
  1754.     }
  1755.     opendir(THISDIR, ".") || return &Error(1, "Opendir of $path failed: $!\n");
  1756.     foreach $file (grep(-f, readdir(THISDIR))) {
  1757.     printf(STDERR "$file\n") if ($debug);
  1758.     $status = &DiffFile($path, $file, $args, $current);
  1759.     if ($status) {
  1760.         return $status;
  1761.     }
  1762.     }
  1763.     if ($recurse) {
  1764.     $status = &AllSubdirs($path, "Diff", $args, $current);
  1765.     }
  1766. }
  1767.  
  1768.  
  1769.  
  1770. #
  1771. # DiffCmd(@modules)
  1772. #
  1773. # Does an rcsdiff on the modules or directories
  1774. #
  1775. # Results: 0 if successful, 1 otherwise
  1776. #
  1777. # Side effects: 
  1778. #
  1779.  
  1780. sub DiffCmd {
  1781.     local(@modules) = @_;
  1782.     local($pwd, $i);
  1783.     local($status) = 0;
  1784.     local($current) = 0;
  1785.     local(@options) = (
  1786.     "R", $OPT_TRUE, *current, "Diff with current version",
  1787.     "l", $OPT_FALSE, *recurse, "Recurse on subdirectories",
  1788.     "b", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1789.     "i", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1790.     "w", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1791.     "t", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1792.     "c", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1793.     "e", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1794.     "f", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1795.     "h", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1796.     "n", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1797.     "r", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1798.     );
  1799.  
  1800.     $recurse = 1;
  1801.     undef($cvsargs);
  1802.     &Opt_Parse(*modules, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  1803.     print "@modules\n" if ($debug);
  1804.     if ($#modules < $[) {
  1805.     push(@modules, ".");
  1806.     }
  1807.     if (-f $modules[0]) {
  1808.     $status = &Lock("r","."); 
  1809.     if ($status) {
  1810.         return $status;
  1811.     }
  1812.     foreach $i (@modules) {
  1813.         &DiffFile(".", $i, $cvsargs, $current);
  1814.     }
  1815.     } else {
  1816.     $status = &Lock("r",@modules);
  1817.     if ($status) {
  1818.         return $status;
  1819.     }
  1820.     $pwd = $ENV{'PWD'};
  1821.  
  1822.     foreach $i (@modules) {
  1823.         printf("DiffCmd $i\n") if ($debug);
  1824.         &Chdir($i) == 0 || return 1; 
  1825.         $status = &Diff($i, $cvsargs, $current);
  1826.         if ($status) {
  1827.         return $status;
  1828.         }
  1829.         &Chdir($pwd) == 0 || return 1; 
  1830.     }
  1831.     }
  1832.     return $status;
  1833. }
  1834.  
  1835. #
  1836. # Cvs($path, $command)
  1837. #
  1838. # Run a cvs command in the current directory and its subdirectories.
  1839. # Any output from the command is printed.  The command is not executed
  1840. # in any "SCVS" subdirectories.
  1841. #
  1842. # Results: 0 if successful, 1 otherwise
  1843. #
  1844. # Side effects: 
  1845. #
  1846. sub Cvs {
  1847.     local($path) = shift;
  1848.     local($command) = shift;
  1849.     local($pwd) = $ENV{'PWD'};
  1850.     local($status) = 0;
  1851.     local($output, $tail);
  1852.  
  1853.     if (!-d "CVS.adm") {
  1854.     return 0;
  1855.     }
  1856.     $tail = substr($path, rindex($path, '/') + 1);
  1857.     if ($tail eq "SCVS") {
  1858.     return 0;
  1859.     }
  1860.     printf("%s\n", $path);
  1861.     system("cvs -d $cvsroot $cvsCmdArgs $command");
  1862.     if ($recurse) {
  1863.     $status = &AllSubdirs($path, "Cvs", $command);
  1864.     }
  1865.     return $status;
  1866. }
  1867.  
  1868.  
  1869. #
  1870. # CvsCmd($command, @modules)
  1871. #
  1872. # Runs a cvs command on each module and its subdirectories.
  1873. # Any output from the command is printed.
  1874. #
  1875. # Results: 0 if successful, 1 otherwise
  1876. #
  1877. # Side effects: 
  1878. #
  1879.  
  1880. sub CvsCmd {
  1881.     local($command) = shift;
  1882.     local(@modules) = @_;
  1883.     local($i, @args);
  1884.     local($status) = 0;
  1885.     local($path);
  1886.     local($pwd) = $ENV{'PWD'};
  1887.     local(@options) = (
  1888.     "l", $OPT_FALSE, *recurse, "Don't recurse on subdirs",
  1889.     "L", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1890.     "R", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1891.     "h", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1892.     "t", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1893.     "b", $OPT_FUNC, "CvsOpt1", $OPT_NULL,
  1894.     "d", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1895.     "l", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1896.     "r", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1897.     "s", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1898.     "w", $OPT_FUNC, "CvsOpt2", $OPT_NULL,
  1899.     );
  1900.  
  1901.  
  1902.     $recurse = 1;
  1903.     undef($cvsargs);
  1904.     &Opt_Parse(*modules, @options, $OPT_OPTIONS_FIRST | $OPT_NO_SPACE);
  1905.  
  1906.     if ($#modules < $[) {
  1907.     push(@modules, ".");
  1908.     }
  1909.     if (-f $modules[0]) {
  1910.     $status = &Lock("r","."); 
  1911.     if ($status) {
  1912.         return $status;
  1913.     }
  1914.     $tmp = "cvs -d $cvsroot $cvsCmdArgs $command $cvsargs @modules";
  1915.     print "$tmp\n" if ($debug);
  1916.     system($tmp);
  1917.     } else {
  1918.     $status = &Lock("r", @modules); 
  1919.     if ($status) {
  1920.         return $status;
  1921.     }
  1922. module: 
  1923.     foreach $i (@modules) {
  1924.         &Chdir($i) == 0 || return 1; 
  1925.         $status = &Cvs($i, $command);
  1926.         &Chdir($pwd) == 0 || return 1; 
  1927.     }
  1928.     }
  1929.     return $status;
  1930. }
  1931.  
  1932.  
  1933.  
  1934. #
  1935. # Exit
  1936. #
  1937. # Exit with a status of 1.
  1938. #
  1939. # Results: Doesn't return
  1940. #
  1941. # Side effects: The script exits.
  1942. #
  1943.  
  1944.  
  1945. sub Exit {
  1946.     exit(1);
  1947. }
  1948.  
  1949.  
  1950. #
  1951. # Usage(@optionArray)
  1952. #
  1953. # Print out help information.
  1954. #
  1955. # Results: None
  1956. #
  1957. # Side effects: Stuff is printed
  1958. #
  1959. sub Usage {
  1960.     local(@options) = @_;
  1961.     local(%info) = (("unpack", "Create symbolic links"),
  1962.             ("checkout", "Checkout a copy of a module"),
  1963.             ("unlock", "Unlock a module"),
  1964.             ("lock", "Lock a module"),
  1965.             ("update", "Update a copy of a module"),
  1966.             ("done", "User is done with a module"),
  1967.             ("commit", "Commit changes to a module"),
  1968.             ("who", "Print a list of users with copies of a module"),
  1969.             ("diff", "Do rcsdiff on files you have changed"),
  1970.             ("status", "Print out rcs status of files"),
  1971.             ("log", "Print rcs log of files"),
  1972.             ("join", "Merge in new vendor release"),
  1973.             ("patch", "Create a patch file"),
  1974.             ("tag", "Tag a version"));
  1975.  
  1976.     &Opt_PrintUsage(@options);
  1977.     printf("\nValid commands are:\n");
  1978.     foreach $i sort ("unpack", "checkout", "unlock", "lock", "update", 
  1979.             "done", "commit", "who", "diff", "status", "log",
  1980.             @cvsCmds) {
  1981.     printf("\t$i\t%s\n", $info{$i});
  1982.     }
  1983. }
  1984.  
  1985. #
  1986. # Error($status, @args)
  1987. #
  1988. # Prints @args to STDERR, and returns $status
  1989. #
  1990. # Results: $status
  1991. #
  1992. # Side effects: Stuff is printed
  1993. #
  1994. sub Error {
  1995.     local($status) = shift;
  1996.     if ($#_ >= $[) {
  1997.     printf(STDERR @_);
  1998.     }
  1999.     return $status;
  2000. }
  2001.  
  2002. #
  2003. # ReadFile($file, $ignoreComments)
  2004. #
  2005. # Reads the contents of the given file.  If $ignoreComments is non-zero
  2006. # then any line beginning with '#' is ignored.  
  2007. #
  2008. # Results: An array containing each line of the file.  If a scalar is
  2009. #     wanted then only the first line is returned.
  2010. #
  2011. # Side effects: 
  2012. #
  2013. sub ReadFile {
  2014.     local($file) = shift;
  2015.     local($ignoreComments) = shift; 
  2016.     local(@contents);
  2017.     open(READ, "$file") ||
  2018.     return &Error(undef, "Open of $file failed: $!\n");
  2019.     if ($ignoreComments) {
  2020.     @contents = grep(!/^#/, <READ>);
  2021.     } else {
  2022.     @contents = <READ>;
  2023.     }
  2024.     close(READ);
  2025.     if ($#contents < $[) {
  2026.     return undef;
  2027.     }
  2028.     if (wantarray) {
  2029.     return @contents;
  2030.     } 
  2031.     return($contents[0]);
  2032. }
  2033.  
  2034. #
  2035. # WriteFile($file, @args)
  2036. #
  2037. # Writes @args to $file.  The file is created if it doesn't exist.
  2038. #
  2039. # Results: 0 if successful, 1 otherwise
  2040. #
  2041. # Side effects:  $file may be created, and it is written.
  2042. #
  2043. sub WriteFile {
  2044.     local($file) = shift;
  2045.     open(WRITE, ">$file") ||
  2046.     return &Error(1, "Open of $file failed: $!\n");
  2047.     print WRITE @_;
  2048.     close(WRITE);
  2049.     return 0;
  2050. }
  2051.  
  2052.  
  2053. #
  2054. # GetModuleName
  2055. #
  2056. # Gets the module name from the name in CVS.adm/Repository and %dirMap.
  2057. #
  2058. # Results: The module name.
  2059. #
  2060. # Side effects:  
  2061. #
  2062. sub GetModuleName {
  2063.     local($dir);
  2064.     local($index);
  2065.     if (!defined(%dirMap)) {
  2066.     &ModMap;
  2067.     }
  2068.     $dir = &ReadFile("CVS.adm/Repository");
  2069.     chop($dir);
  2070.     printf("$dir\n") if ($debug);
  2071.     if (!defined($dir)) {
  2072.     return undef;
  2073.     }
  2074.     while($dir ne "") {
  2075.     if (defined($dirMap{$dir})) {
  2076.         printf("Module $dirMap{$dir}\n") if ($debug);
  2077.         return $dirMap{$dir};
  2078.     }
  2079.     $index = rindex($dir, '/');
  2080.     if ($index < $[) {
  2081.         last;
  2082.         return $dir;
  2083.     }
  2084.     $dir = substr($dir, 0, $index);
  2085.     }
  2086.     return $dir;
  2087. }
  2088.  
  2089.  
  2090. #
  2091. # Chdir($dir)
  2092. #
  2093. # Changes the current working directory to $dir.  If the command fails
  2094. # an error message is printed. 
  2095. #
  2096. # Results: 0 if successful, 1 otherwise
  2097. #
  2098. # Side effects:  The current working directory is changed, and $ENV{'PWD'}
  2099. #     set to the new working directory.
  2100. #
  2101. sub Chdir {
  2102.     &chdir($_[0]) ||
  2103.     return &Error(1, "Chdir to %s from %s failed: $!\n", 
  2104.         $_[0], $ENV{'PWD'});
  2105.     return 0;
  2106. }
  2107.  
  2108. #
  2109. # ModMap
  2110. #
  2111. # Creates a mapping of module name to its subdirectory in the repository,
  2112. # and a mapping from the subdirectory to the module name.
  2113. #
  2114. # Results: 0 if successful, 1 otherwise
  2115. #
  2116. # Side effects:  The %modMap and %dirMap are filled in.
  2117. #
  2118.  
  2119. sub ModMap {
  2120.     local($module, $dir);
  2121.     open(MOD, "cvs -d $cvsroot co -c |") ||
  2122.     return &Error(1, "Can't do \"cvs co -c\"\n");
  2123.     undef %modMap;
  2124.     while(<MOD>) {
  2125.     if (/^(\S+)\s+(\S+)/) {
  2126.         $modMap{$1} = $2;
  2127.         $dirMap{$2} = $1;
  2128.     }
  2129.     }
  2130.     close(MOD);
  2131. }
  2132.  
  2133. #
  2134. # Main
  2135. #
  2136. #
  2137. $SIG{'INT'} = Exit;
  2138. &initpwd;
  2139. $tmpfile = "#SCVS.$$";
  2140. $status = 0;
  2141. if (&Config) {
  2142.     exit(1);
  2143. }
  2144. $command = shift;
  2145. if (!defined($command)) {
  2146.     &Usage(@options);
  2147.     exit(1);
  2148. }
  2149. printf("$command: %s\n", join(' ', @ARGV)) if ($debug);
  2150.  
  2151. if (($command eq "pack") || ($command eq "unpack")) {
  2152.     local(@options) = ("l", $OPT_FALSE, *recurse, "Recurse on subdirectories");
  2153.     &Opt_Parse(*ARGV, @options, 0);
  2154.     $status = &PackCmd($command, @ARGV);
  2155. } elsif (($command eq "checkout") || ($command eq "co")) {
  2156.     $command = "checkout";
  2157.     $status = &Checkout(@ARGV);
  2158. } elsif ($command eq "unlock") {
  2159.     $status = &UnlockCmd(@ARGV);
  2160. } elsif ($command eq "lock") {
  2161.     $status = &LockCmd(@ARGV);
  2162.     undef(@locks);
  2163. } elsif ($command eq "update") {
  2164.     $status = &UpdateCmd(1, @ARGV);
  2165. } elsif ($command eq "done") {
  2166.     $status = &DoneCmd(@ARGV);
  2167. } elsif (($command eq "commit") || ($command eq "ci")) {
  2168.     $status = &CommitCmd(@ARGV);
  2169. } elsif ($command eq "who") {
  2170.     $status = &WhoCmd(@ARGV);
  2171. } elsif ($command eq "add") {
  2172.     $status = &AddCmd(@ARGV);
  2173. } elsif ($command eq "remove") {
  2174.     $status = &RemoveCmd(@ARGV);
  2175. } elsif ($command eq "info") {
  2176.     $status = &InfoCmd(@ARGV);
  2177. } elsif ($command eq "diff") {
  2178.     $status = &DiffCmd(@ARGV);
  2179. } elsif (($command eq "status") || ($command eq "log")) {
  2180.     $status = &CvsCmd($command, @ARGV);
  2181. } elsif (grep($command eq $_, @cvsCmds)) {
  2182.     system("cvs -d $cvsroot $cvsCmdArgs $command @ARGV");
  2183.     $status = 0;
  2184. } else {
  2185.     printf("Bad command: $command\n");
  2186.     &Usage(@options);
  2187.     exit(1);
  2188. }
  2189.  
  2190. # Unlock any modules we may have locked.
  2191.  
  2192. if ($#locks >= $[) {
  2193.     &Unlock(0, @locks);
  2194. }
  2195. if ($status) {
  2196.     printf("$command failed\n");
  2197. }
  2198. exit($status);
  2199.